﻿// @brief
// @file Field.cs
// @date 2024年08月16日
// @author doublecat
// 
// Copyright (C) &lt;2024&gt;  &lt;doublecat&gt;
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

using System.Text.RegularExpressions;
using System.Xml.Serialization;

namespace doublecat.mcu.svd;

/// <summary>
/// 位域
/// </summary>
public class Field : IDimGroup {
    private uint? _lsb;

    /// <summary>
    /// 
    /// </summary>
    [XmlElement("access")]
    public string? Access { get; set; }

    /// <summary>
    /// 
    /// </summary>
    [XmlElement("bitOffset")]
    public uint BitOffset { get; set; }

    /// <summary>
    /// 
    /// </summary>
    [XmlElement("bitRange")]
    public string? BitRange {
        get => $"[{Msb}:{Lsb}]";
        set {
            if (!string.IsNullOrEmpty(value)) {
                // 解析格式[msb:lsb]
                Regex regex = new Regex(@"\[(\w+):(\w+)\]");
                Match match = regex.Match(value);
                Lsb = uint.Parse(match.Groups[2].Value);
                Msb = uint.Parse(match.Groups[1].Value);
            }
        }
    }

    /// <summary>
    /// 
    /// </summary>
    [XmlElement("bitWidth")]
    public uint BitWidth { get; set; }

    /// <summary>
    /// 
    /// </summary>
    [XmlAttribute("derivedFrom")]
    public string? DerivedFrom { get; set; }

    /// <summary>
    /// 
    /// </summary>
    [XmlElement("description")]
    public string? Description { get; set; }

    /// <summary>
    /// 
    /// </summary>
    [XmlElement("enumeratedValues")]
    public EnumeratedValueCollection? Enumerated { get; set; }

    /// <summary>
    /// 位域的最低位
    /// </summary>
    [XmlElement("lsb")]
    public uint Lsb {
        get {
            if (_lsb == null || _lsb != BitOffset)
                throw new InvalidOperationException("lsb is null error!");
            return BitOffset;
        }
        set => _lsb = BitOffset = value;
    }

    /// <summary>
    /// 
    /// </summary>
    [XmlElement("modifiedWriteValues")]
    public string? ModifiedWriteValues { get; set; }

    /// <summary>
    /// 位域的最高位
    /// </summary>
    /// <remarks>设置此属性时，需要使用lsb属性值进行计算，所以如果不先设置lsb属性，则会出现问题！</remarks>
    [XmlElement("msb")]
    public uint Msb {
        get {
            if (_lsb == null)
                throw new InvalidOperationException("must set lsb before msb!");
            return BitOffset + BitWidth - 1;
        }
        set {
            if (_lsb == null)
                throw new InvalidOperationException("must set lsb before msb!");
            BitWidth = value - BitOffset + 1;
        }
    }

    [XmlElement("name")]
    public required string Name { get; set; }

    [XmlElement("readAction")]
    public string? ReadAction { get; set; }

    [XmlElement("writeConstraint")]
    public string? Constraint { get; set; }

    /// <summary>
    /// 位域所属的寄存器
    /// </summary>
    public required Register Parent { get; set; }

    #region IDimGroup Members

    [XmlElement("dim")]
    public string? Dim { get; set; }

    [XmlElement("dimIncrement")]
    public string? DimIncrement { get; set; }

    [XmlElement("dimIndex")]
    public string? DimIndex { get; set; }

    [XmlElement("dimName")]
    public string? DimName { get; set; }

    [XmlElement("dimArrayIndex")]
    public DimArrayIndex? ArrayIndex { get; set; }

    #endregion
}
